const http = require('http');
const url = require('url');
const qs = require('querystring');
const fs = require('fs');
const path = require('path');

/**
 *
 * @param {*} fileInfo 接收一个二进制文件信息流
 * @returns filename
 */
const getFilenameFromStream = (fileInfo = '') => {
  // 2. 获取文件content信息
  const contentInfoArr = fileInfo.split('\r\n');
  const contentDisposition = contentInfoArr.find(item => item.includes('Content-Disposition'));
  let { filename } = qs.parse(contentDisposition, '; ');
  try {
    if (Array.isArray(filename)) {
      filename = filename[0];
    }
    return filename.match(/"(.+)"/)[1];
  } catch (error) {
    return filename;
  }
};

const randomFilename = () => new Date().getTime();

const writeFile = async (stream, extname) => {
  const filename = `${randomFilename()}.${extname}`;
  const filepath = path.resolve(__dirname, `./files/${filename}`);
  fs.promises
    .writeFile(filepath, stream, 'binary')
    .then(res => Promise.resolve(res))
    .catch(err => Promise.reject(err));
};

// 文件信息和文件内容的分割符
const sep = '\r\n\r\n';
const service = http.createServer((req, res) => {
  const { method } = req;
  if (method === 'POST') {
    const { pathname } = url.parse(req.url);
    req.setEncoding('binary'); // 上传文件一定要设置呈binary
    let rawFileStream = '';
    if (pathname === '/upload_file') {
      const contentType = req.headers['content-type'];
      const boundaryStr = contentType.split('=')[1];
      req.on('data', data => {
        rawFileStream += data;
      });
      req.on('end', async () => {
        // debug log
        // console.log(JSON.stringify(rawFileStream.substring(0, 400))); // 头部信息
        // console.log(JSON.stringify(rawFileStream.substring(rawFileStream.length - 200))); // 尾部信息
        // 1. 去除流中的boundary分割符，boundary开始前面有--即：--boundary，结束时boundary--
        const beginBoundaryRegexp = new RegExp(`--${boundaryStr}\r\n`);
        const endBoundaryRegexp = new RegExp(`--${boundaryStr}--\r\n`);
        rawFileStream = rawFileStream.replace(beginBoundaryRegexp, '');
        rawFileStream = rawFileStream.replace(endBoundaryRegexp, '');

        // 2. 获取上传内容开始位置
        const splitIndex = rawFileStream.indexOf(sep);
        const fileInfo = rawFileStream.substring(0, splitIndex);

        // 3. 获取文件名
        const filename = getFilenameFromStream(fileInfo);
        const _list = filename.split('.');
        const extname = _list[_list.length - 1]; // 扩展名

        // 4. 获取上传文件内容的stream
        const fileStream = rawFileStream.split(sep)[1];
        // debug log
        // console.log(JSON.stringify(fileStream.substring(0, 100))); // 流头部信息
        // console.log(JSON.stringify(fileStream.substring(fileStream.length - 100))); // 流尾部信息

        // 5. 写入文件
        const dirpath = path.resolve(__dirname, 'files');
        try {
          const stat = fs.statSync(dirpath);
          if (stat.isDirectory()) {
            await writeFile(fileStream, extname);
            res.end('upload success');
          }
        } catch (error) {
          fs.mkdirSync(dirpath);
          await writeFile(fileStream, extname);
          res.end('upload success');
        }
      });
    }
  } else {
    res.writeHead(401).end('method not allow !');
  }
});

service.listen(8080, () => {
  console.log('service start success~');
});
